// $Id: list.c 2 2011-10-17 17:49:43Z cedric.shih@gmail.com $
/*
 * Copyright 2011 Cedric Shih (cedric dot shih at gmail dot com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#include <stdlib.h>
#include <errno.h>
#include <string.h>

#include "log.h"
#include "buf.h"

#include "list.h"

int zcbuffer_list_add(struct zcbuffer_list *list, struct zcbuffer *buffer)
{
	int rc;

	if (buffer->item.buffer) {
		rc = EPERM;
		zcbuffer_error("already added to another list: %p", buffer);
		goto finally;
	}

	zcbuffer_trace("adding buffer to %p: %p", list, buffer);

	buffer->item.buffer = buffer;
	TAILQ_INSERT_TAIL(list, &buffer->item, entry);
	rc = 0;

finally:
	return rc;
}

int zcbuffer_list_remove(struct zcbuffer_list *list, struct zcbuffer *buffer)
{
	int rc;

	if (!buffer->item.buffer) {
		rc = EPERM;
		zcbuffer_error("not added to any list: %p", buffer);
		goto finally;
	}

	zcbuffer_trace("removing buffer from %p: %p", list, buffer);

	TAILQ_REMOVE(list, &buffer->item, entry);
	buffer->item.buffer = NULL;
	rc = 0;

finally:
	return rc;
}

int zcbuffer_list_clear(struct zcbuffer_list *list)
{
	int rc;
	struct zcbuffer_item *item;

	zcbuffer_trace("clearing %p", list);

	while ((item = TAILQ_FIRST(list))) {
		if ((rc = zcbuffer_list_remove(list, item->buffer))) {
			zcbuffer_error("failed to remove: %s", strerror(rc));
			goto finally;
		}
	}

	rc = 0;

finally:
	return rc;
}

int zcbuffer_list_add_cache(struct zcbuffer_list *list,
		struct zcbuffer **ptr)
{
	int rc;
	struct zcbuffer *buffer;

	zcbuffer_trace("creating cache to %p", list);

	if (!(buffer = calloc(1, sizeof(*buffer)))) {
		rc = errno ? errno : -1;
		zcbuffer_error("calloc: %s", strerror(rc));
		goto finally;
	}

	zcbuffer_init(buffer);

	if ((rc = zcbuffer_list_add(list, buffer))) {
		zcbuffer_error("failed to add: %s", strerror(rc));
		goto finally;
	}

	*ptr = buffer;
	rc = 0;

finally:
	return rc;
}

int zcbuffer_list_remove_cache(struct zcbuffer_list *list,
		struct zcbuffer *buffer)
{
	int rc;

	if ((rc = zcbuffer_list_remove(list, buffer))) {
		zcbuffer_error("failed to remove: %s", strerror(rc));
		goto finally;
	}

	free(buffer);
	rc = 0;

finally:
	return rc;
}

int zcbuffer_list_clear_caches(struct zcbuffer_list *list)
{
	int rc;
	struct zcbuffer_item *item;

	zcbuffer_trace("clearing caches in %p", list);

	while ((item = TAILQ_FIRST(list))) {
		if ((rc = zcbuffer_list_remove_cache(list, item->buffer))) {
			zcbuffer_error("failed to remove: %s", strerror(rc));
			goto finally;
		}
	}

	rc = 0;

finally:
	return rc;
}
